﻿#include "pch.h"
#include "OpenGLBuffer.h"

#include "core/Macro.h"
#include "core/log/LogSystem.h"
#include "function/RenderSystem/AbstractRenderer.h"

namespace Engine
{
	// -----------------------------------------------------------------------------------------------------------------
	//    OpenGLVertexBuffer        ------------------------------------------------------------------------------------
	// -----------------------------------------------------------------------------------------------------------------

	OpenGLVertexBuffer::OpenGLVertexBuffer(const float* vertices, const uint32_t size)
	{
		glCreateBuffers(1, &rendererID);
		glBindBuffer(GL_ARRAY_BUFFER, rendererID);
		glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_STATIC_DRAW);
	}

	OpenGLVertexBuffer::~OpenGLVertexBuffer()
	{
		glDeleteBuffers(1, &rendererID);
	}

	void OpenGLVertexBuffer::Bind() const
	{
		glBindBuffer(GL_ARRAY_BUFFER, rendererID);
	}

	void OpenGLVertexBuffer::UnBind() const
	{
		glBindBuffer(GL_ARRAY_BUFFER, 0);
	}

	void OpenGLVertexBuffer::SetLayout(const BufferLayout& layout) // NOLINT(clang-diagnostic-shadow)
	{
		this->layout = layout;
	}

	VertexBuffer* VertexBuffer::Create(const float* vertices, const uint32_t size)
	{
		switch (AbstractRenderer::GetAPI())
		{
		case RendererAPI::API::None:
			ENGINE_ASSERT(false, "无 Renderer API！")
			return nullptr;
#if PLATFORM_WINDOWS
		case RendererAPI::API::OpenGL:
			return new OpenGLVertexBuffer(vertices, size);
		case RendererAPI::API::Direct3D:
			return nullptr;
		case RendererAPI::API::Vulcan:
			return nullptr;
#elif PLATFORM_MAC
		case RendererAPI::Metal:
			return nullptr;
#endif
		default:
			ENGINE_ASSERT(false, "未知的 Renderer API！")
			return nullptr;
		}
	}

	// -----------------------------------------------------------------------------------------------------------------
	//    OpenGLIndexBuffer         ------------------------------------------------------------------------------------
	// -----------------------------------------------------------------------------------------------------------------

	OpenGLIndexBuffer::OpenGLIndexBuffer(const unsigned int* indices, const uint32_t count) : count(count)
	{
		glCreateBuffers(1, &rendererID);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, rendererID);
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, count * sizeof(uint32_t), indices, GL_STATIC_DRAW);
	}

	OpenGLIndexBuffer::~OpenGLIndexBuffer()
	{
		glDeleteBuffers(1, &rendererID);
	}

	void OpenGLIndexBuffer::Bind() const
	{
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, rendererID);
	}

	void OpenGLIndexBuffer::UnBind() const
	{
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
	}

	uint32_t OpenGLIndexBuffer::GetCount() const
	{
		return this->count;
	}

	IndexBuffer* IndexBuffer::Create(const unsigned int* indices, const uint32_t count)
	{
		switch (AbstractRenderer::GetAPI())
		{
		case RendererAPI::API::None:
			ENGINE_ASSERT(false, "无 Renderer API！")
			return nullptr;
#if PLATFORM_WINDOWS
		case RendererAPI::API::OpenGL:
			return new OpenGLIndexBuffer(indices, count);
		case RendererAPI::API::Direct3D:
			return nullptr;
		case RendererAPI::API::Vulcan:
			return nullptr;
#elif PLATFORM_MAC
		case RendererAPI::Metal:
			return nullptr;
#endif
		default:
			ENGINE_ASSERT(false, "未知的 Renderer API！")
			return nullptr;
		}
	}

	// -----------------------------------------------------------------------------------------------------------------
	//    OpenGLVertexArray         ------------------------------------------------------------------------------------
	// -----------------------------------------------------------------------------------------------------------------

	static GLenum ShaderDateTypeToOpenGLBaseType(const ShaderDateType type)
	{
		switch (type)
		{
			case ShaderDateType::Float: return GL_FLOAT;
			case ShaderDateType::Float2: return GL_FLOAT;
			case ShaderDateType::Float3: return GL_FLOAT;
			case ShaderDateType::Float4: return GL_FLOAT;
			case ShaderDateType::Mat3: return GL_FLOAT;
			case ShaderDateType::Mat4: return GL_FLOAT;
			case ShaderDateType::Int: return GL_INT;
			case ShaderDateType::Int2: return GL_INT;
			case ShaderDateType::Int3: return GL_INT;
			case ShaderDateType::Int4: return GL_INT;
			case ShaderDateType::Bool: return GL_BOOL;
			default:
			{
				ENGINE_ASSERT(false, "未知 Shader Data 类型！")
				return 0;
			}
		}
	}

	OpenGLVertexArray::OpenGLVertexArray()
	{
		glGenVertexArrays(1, &rendererID);
	}

	OpenGLVertexArray::~OpenGLVertexArray()
	{
		glDeleteVertexArrays(1, &rendererID);
	}

	void OpenGLVertexArray::Bind() const
	{
		glBindVertexArray(rendererID);
	}

	void OpenGLVertexArray::UnBind() const
	{
		glBindVertexArray(0);
	}

	void OpenGLVertexArray::AddVertexBuffer(const Ref<VertexBuffer>& vertexBuffer)
	{
		glBindVertexArray(rendererID);
		vertexBuffer->Bind();

		uint32_t index = 0;
		const auto& layout = vertexBuffer->GetLayout();
		for (const auto& element : layout)
		{
			glEnableVertexAttribArray(index);
			glVertexAttribPointer(index, static_cast<GLint>(element.GetElementCount()),
			                      ShaderDateTypeToOpenGLBaseType(element.type),
			                      element.normalized ? GL_TRUE : GL_FALSE, static_cast<GLsizei>(layout.GetStride()),
			                      reinterpret_cast<void*>(element.offset));
			index++;
		}

		this->vertexBuffers.push_back(vertexBuffer);
	}

	void OpenGLVertexArray::SetIndexBuffer(const Ref<IndexBuffer>& indexBuffer)
	{
		glBindVertexArray(rendererID);
		indexBuffer->Bind();

		this->indexBuffer = indexBuffer;
	}

	VertexArray* VertexArray::Create()
	{
		switch (AbstractRenderer::GetAPI())
		{
		case RendererAPI::API::None:
			ENGINE_ASSERT(false, "无 Renderer API！")
			return nullptr;
#if PLATFORM_WINDOWS
		case RendererAPI::API::OpenGL:
			return new OpenGLVertexArray();
		case RendererAPI::API::Direct3D:
			return nullptr;
		case RendererAPI::API::Vulcan:
			return nullptr;
#elif PLATFORM_MAC
				case RendererAPI::Metal:
					return nullptr;
#endif
		default:
			ENGINE_ASSERT(false, "未知的 Renderer API！")
			return nullptr;
		}
	}
}
